/* Utility routines.
 *
 * Copyright 2008 Cian Synnott.
 * 
 * $Id: utility.c 28 2009-12-04 03:01:17Z cian $
 */
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

#include "log.h"
#include "utility.h"

/* See utility.h */
int set_nonblocking(int fd) {
  int flags;

  flags = fcntl(fd, F_GETFL);
  if (flags < 0)
    return -1;

  return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

/* See utility.h */
char *remainder_after_first_token(char *current) {
  while (*current && *current != ' ')
    current++;
  while (*current && *current == ' ')
    *(current++) = '\0';
  return current;
}

/* See utility.h */
char *create_path(const char *format, ...) {
  char *buffer;
  int written;
  va_list args;

  if ((buffer = malloc(PATH_MAX)) == NULL)
    return NULL;

  va_start(args, format);
  written = vsnprintf(buffer, PATH_MAX, format, args);
  va_end(args);

  if (written < 0 || written >= PATH_MAX) {
    free(buffer);
    return NULL;
  }

  return buffer;
}

/* See utility.h */
int connect_to_ohaigent(int quiet) {
  struct sockaddr_un addr;
  char *home = NULL;
  char *sock = NULL;
  int fd;

  if ((home = getenv("HOME")) == NULL) {
    if (!quiet)
      log_err("Failed to find HOME environment variable. Weird!");
    exit(EXIT_FAILURE);
  }

  sock = create_path("%s/.ohai/socket", home);
  if (NULL == sock) {
    if (!quiet)
      log_err("Error creating server socket pathname.");
    exit(EXIT_FAILURE);
  }

  if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
    if (!quiet)
      log_err("creating UNIX socket: %s", strerror(errno));
    exit(EXIT_FAILURE);
  }

  addr.sun_family = AF_UNIX;
  strncpy(addr.sun_path, sock, sizeof(addr.sun_path));
  if (connect(fd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) == -1) {
    if (!quiet)
      log_err("connecting UNIX socket %s: %s", sock, strerror(errno));
    exit(EXIT_FAILURE);
  }
  free(sock);

  return fd;
}

/* See utility.h */
void socket_write(int fd, char *string) {
  ssize_t ret;
  size_t len = strlen(string);
  
  while (len != 0 && (ret = write(fd, string, len)) != 0) {
    if (-1 == ret) {
      if (errno == EINTR)
        continue;
      log_err("Error in write to client fd %d: %s", fd, strerror(errno));
      break;
    }
    len -= ret;
    string += ret;
  }
}

/* See utility.h */
int socket_read(int fd, char *buffer, size_t size) {
  ssize_t len;

start:
  len = read(fd, buffer, size);
  if (len == -1) {
    if (errno == EINTR)
      goto start;
    if (errno == EAGAIN) {
      log_warn("no data available reading client fd %d", fd);
      return -1;
    }
    else {
      log_err("error reading on client fd %d: %s", fd, strerror(errno));
      return -1;
    }
  }

  buffer[len] = '\0';

  return len;
}

/* See utility.h */
void kill_newline(char *buffer) {
  size_t nl = strlen(buffer) - 1;
  if (buffer[nl] == '\n')
    buffer[nl] = '\0';
}
